# src/frontend/unified_interface.py
"""
统一窗口界面 - 修复Pygame初始化顺序
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import pygame
import threading
import time
import json
import os
from typing import Dict, Any, Optional, Callable

from .live_renderer import LiveRenderer


class UnifiedGameInterface:
    """统一游戏界面 - 集成游戏画面和控制面板"""

    def __init__(self, backend_interface=None):
        self.backend_interface = backend_interface

        # 窗口设置
        self.window_width = 1200
        self.window_height = 800
        self.game_width = 700
        self.game_height = 700
        self.panel_width = 480

        # 创建主窗口
        self.root = tk.Tk()
        self.root.title("游戏智能体 - 统一界面")
        self.root.geometry(f"{self.window_width}x{self.window_height}")
        self.root.resizable(False, False)

        # 游戏状态
        self.ai_control = True
        self.is_training = False
        self.model_frozen = False
        self.current_game_state = None

        # 回放状态
        self.playback_mode = False
        self.is_playing = False
        self.replay_frames = []
        self.replay_index = 0
        self.playback_speed = 1.0

        # 统计变量
        self.stats_vars = {}
        self.message_log = []
        self.max_messages = 100

        # 渲染器相关 - 延迟初始化
        self.renderer = None
        self.pygame_surface = None
        self.tk_canvas = None
        self.pygame_initialized = False

        # 线程控制
        self.running = True
        self.update_thread = None

        # 初始化界面（不包含Pygame部分）
        self._init_interface()

        # 延迟初始化Pygame，确保在窗口显示后进行
        self.root.after(100, self._init_pygame_delayed)

        print("统一游戏界面初始化完成")

    def _init_pygame_delayed(self):
        """延迟初始化Pygame和渲染器"""
        try:
            print("开始初始化Pygame...")

            # 初始化Pygame
            if not pygame.get_init():
                pygame.init()

            # 创建一个隐藏的pygame display来确保video mode被设置
            os.environ['SDL_VIDEODRIVER'] = 'dummy'  # 使用dummy驱动避免创建实际窗口
            pygame.display.set_mode((1, 1))  # 创建最小窗口

            # 创建游戏表面
            self.pygame_surface = pygame.Surface((self.game_width, self.game_height))

            # 现在初始化渲染器
            self.renderer = LiveRenderer(self.game_width, self.game_height)

            self.pygame_initialized = True
            print("Pygame和渲染器初始化成功")

            # 启动更新线程
            self._start_update_thread()

        except Exception as e:
            print(f"Pygame初始化失败: {e}")
            import traceback
            traceback.print_exc()

    def _init_interface(self):
        """初始化界面布局（不包含Pygame）"""
        # 创建主框架
        main_frame = tk.Frame(self.root, bg='#2b2b2b')
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 左侧游戏画面区域
        self._create_game_area(main_frame)

        # 右侧控制面板区域
        self._create_control_panel(main_frame)

    def _create_game_area(self, parent):
        """创建游戏画面区域"""
        game_frame = tk.Frame(parent, bg='#1a1a1a', width=self.game_width, height=self.game_height)
        game_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)
        game_frame.pack_propagate(False)

        # 游戏画布
        self.tk_canvas = tk.Canvas(
            game_frame,
            width=self.game_width,
            height=self.game_height,
            bg='#000000',
            highlightthickness=0
        )
        self.tk_canvas.pack(expand=True)

        # 添加初始化提示
        self.tk_canvas.create_text(
            self.game_width // 2, self.game_height // 2,
            text="正在初始化游戏引擎...",
            fill="white",
            font=("Arial", 16)
        )

        # 游戏控制按钮
        game_controls = tk.Frame(game_frame, bg='#1a1a1a')
        game_controls.pack(fill=tk.X, pady=5)

        tk.Button(game_controls, text="重置游戏", command=self._reset_game,
                  bg='#4a4a4a', fg='white', bd=0, pady=5).pack(side=tk.LEFT, padx=2)
        tk.Button(game_controls, text="切换控制", command=self._toggle_control,
                  bg='#4a4a4a', fg='white', bd=0, pady=5).pack(side=tk.LEFT, padx=2)

        # 回放控制按钮
        replay_controls = tk.Frame(game_frame, bg='#1a1a1a')
        replay_controls.pack(fill=tk.X, pady=2)

        tk.Button(replay_controls, text="打开回放", command=self._open_replay,
                  bg='#3a4a5a', fg='white', bd=0, pady=3).pack(side=tk.LEFT, padx=2)
        tk.Button(replay_controls, text="播放", command=self._play_replay,
                  bg='#3a5a3a', fg='white', bd=0, pady=3).pack(side=tk.LEFT, padx=2)
        tk.Button(replay_controls, text="暂停", command=self._pause_replay,
                  bg='#5a3a3a', fg='white', bd=0, pady=3).pack(side=tk.LEFT, padx=2)
        tk.Button(replay_controls, text="停止回放", command=self._stop_replay,
                  bg='#5a4a3a', fg='white', bd=0, pady=3).pack(side=tk.LEFT, padx=2)

        # 回放进度条
        self.replay_progress = tk.Scale(
            game_frame, from_=0, to=100, orient=tk.HORIZONTAL,
            bg='#2a2a2a', fg='white', highlightthickness=0,
            command=self._on_replay_seek
        )
        self.replay_progress.pack(fill=tk.X, pady=2)

    def _create_control_panel(self, parent):
        """创建控制面板区域"""
        panel_frame = tk.Frame(parent, bg='#2a2a3a', width=self.panel_width)
        panel_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10, pady=10)
        panel_frame.pack_propagate(False)

        # 控制面板内容
        self._create_training_section(panel_frame)
        self._create_model_section(panel_frame)
        self._create_stats_section(panel_frame)
        self._create_message_section(panel_frame)

    def _create_training_section(self, parent):
        """创建训练控制区域"""
        training_frame = tk.LabelFrame(parent, text="训练控制", bg='#2a2a3a', fg='white', font=('Arial', 10, 'bold'))
        training_frame.pack(fill=tk.X, pady=5, padx=5)

        # 训练按钮
        button_frame = tk.Frame(training_frame, bg='#2a2a3a')
        button_frame.pack(fill=tk.X, pady=5)

        self.start_training_btn = tk.Button(
            button_frame, text="开始训练", command=self._start_training,
            bg='#3a5a3a', fg='white', bd=0, pady=5, width=10
        )
        self.start_training_btn.pack(side=tk.LEFT, padx=2)

        self.stop_training_btn = tk.Button(
            button_frame, text="停止训练", command=self._stop_training,
            bg='#5a3a3a', fg='white', bd=0, pady=5, width=10, state=tk.DISABLED
        )
        self.stop_training_btn.pack(side=tk.LEFT, padx=2)

        # 训练参数
        params_frame = tk.Frame(training_frame, bg='#2a2a3a')
        params_frame.pack(fill=tk.X, pady=5)

        # 学习率
        tk.Label(params_frame, text="学习率:", bg='#2a2a3a', fg='white').grid(row=0, column=0, sticky=tk.W)
        self.learning_rate_var = tk.DoubleVar(value=0.001)
        tk.Entry(params_frame, textvariable=self.learning_rate_var, width=8, bg='#3a3a4a', fg='white').grid(row=0,
                                                                                                            column=1,
                                                                                                            padx=5)

        # 探索率
        tk.Label(params_frame, text="探索率:", bg='#2a2a3a', fg='white').grid(row=0, column=2, sticky=tk.W)
        self.epsilon_var = tk.DoubleVar(value=0.1)
        tk.Entry(params_frame, textvariable=self.epsilon_var, width=8, bg='#3a3a4a', fg='white').grid(row=0, column=3,
                                                                                                      padx=5)

        # 训练速度
        tk.Label(params_frame, text="训练速度:", bg='#2a2a3a', fg='white').grid(row=1, column=0, sticky=tk.W, pady=5)
        self.speed_var = tk.DoubleVar(value=1.0)
        speed_scale = tk.Scale(
            params_frame, from_=0.1, to=5.0, variable=self.speed_var, orient=tk.HORIZONTAL,
            bg='#2a2a3a', fg='white', highlightthickness=0, resolution=0.1
        )
        speed_scale.grid(row=1, column=1, columnspan=3, sticky=tk.EW, padx=5, pady=5)

    def _create_model_section(self, parent):
        """创建模型管理区域"""
        model_frame = tk.LabelFrame(parent, text="模型管理", bg='#2a2a3a', fg='white', font=('Arial', 10, 'bold'))
        model_frame.pack(fill=tk.X, pady=5, padx=5)

        button_frame = tk.Frame(model_frame, bg='#2a2a3a')
        button_frame.pack(fill=tk.X, pady=5)

        tk.Button(button_frame, text="保存模型", command=self._save_model,
                  bg='#4a4a5a', fg='white', bd=0, pady=3, width=10).pack(side=tk.LEFT, padx=2)
        tk.Button(button_frame, text="加载模型", command=self._load_model,
                  bg='#4a5a4a', fg='white', bd=0, pady=3, width=10).pack(side=tk.LEFT, padx=2)

        # 模型状态
        self.frozen_var = tk.BooleanVar(value=False)
        tk.Checkbutton(
            model_frame, text="冻结模型", variable=self.frozen_var, command=self._toggle_frozen,
            bg='#2a2a3a', fg='white', selectcolor='#3a3a4a', activebackground='#2a2a3a'
        ).pack(pady=5)

    def _create_stats_section(self, parent):
        """创建统计信息区域"""
        stats_frame = tk.LabelFrame(parent, text="统计信息", bg='#2a2a3a', fg='white', font=('Arial', 10, 'bold'))
        stats_frame.pack(fill=tk.X, pady=5, padx=5)

        # 创建统计网格
        stats_grid = tk.Frame(stats_frame, bg='#2a2a3a')
        stats_grid.pack(fill=tk.X, pady=5)

        stats_info = [
            ("轮次", "episode", 0),
            ("回合", "round", 0),
            ("得分", "score", 0),
            ("生命值", "health", 100),
            ("探索率", "epsilon", 0.1),
            ("平均奖励", "avg_reward", 0.0)
        ]

        for i, (label, key, default) in enumerate(stats_info):
            row = i // 2
            col = (i % 2) * 2

            tk.Label(stats_grid, text=f"{label}:", bg='#2a2a3a', fg='white', font=('Arial', 9)).grid(
                row=row, column=col, sticky=tk.W, padx=5, pady=2
            )

            var = tk.StringVar(value=str(default))
            self.stats_vars[key] = var
            tk.Label(stats_grid, textvariable=var, bg='#2a2a3a', fg='#4af', font=('Arial', 9, 'bold')).grid(
                row=row, column=col + 1, sticky=tk.W, padx=5, pady=2
            )

    def _create_message_section(self, parent):
        """创建消息日志区域"""
        message_frame = tk.LabelFrame(parent, text="消息日志", bg='#2a2a3a', fg='white', font=('Arial', 10, 'bold'))
        message_frame.pack(fill=tk.BOTH, expand=True, pady=5, padx=5)

        # 消息文本框
        text_frame = tk.Frame(message_frame, bg='#2a2a3a')
        text_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        self.message_text = tk.Text(
            text_frame, wrap=tk.WORD, height=6,
            bg='#1a1a2a', fg='white', font=('Consolas', 9),
            insertbackground='white'
        )

        scrollbar = tk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.message_text.yview)
        self.message_text.configure(yscrollcommand=scrollbar.set)

        self.message_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 清除按钮
        tk.Button(message_frame, text="清除日志", command=self._clear_messages,
                  bg='#4a3a3a', fg='white', bd=0, pady=3).pack(pady=5)

    # =========================
    # 游戏控制方法
    # =========================

    def _reset_game(self):
        """重置游戏"""
        if self.backend_interface:
            self.backend_interface.reset_game()
            self.add_message("游戏已重置")
        else:
            self.add_message("后端未连接")

    def _toggle_control(self):
        """切换控制模式"""
        self.ai_control = not self.ai_control
        if self.backend_interface:
            self.backend_interface.set_ai_control(self.ai_control)

        mode = "AI控制" if self.ai_control else "手动控制"
        self.add_message(f"切换到{mode}模式")

    def _start_training(self):
        """开始训练"""
        if self.backend_interface:
            params = {
                "learning_rate": self.learning_rate_var.get(),
                "epsilon": self.epsilon_var.get(),
                "speed": self.speed_var.get()
            }

            self.backend_interface.start_training(params)
            self.is_training = True

            self.start_training_btn.config(state=tk.DISABLED)
            self.stop_training_btn.config(state=tk.NORMAL)

            self.add_message("训练已开始")

    def _stop_training(self):
        """停止训练"""
        if self.backend_interface:
            self.backend_interface.stop_training()
            self.is_training = False

            self.start_training_btn.config(state=tk.NORMAL)
            self.stop_training_btn.config(state=tk.DISABLED)

            self.add_message("训练已停止")

    def _save_model(self):
        """保存模型"""
        if self.backend_interface:
            filename = filedialog.asksaveasfilename(
                title="保存模型",
                filetypes=[("模型文件", "*.pth"), ("所有文件", "*.*")],
                defaultextension=".pth"
            )
            if filename:
                model_name = os.path.splitext(os.path.basename(filename))[0]
                saved_path = self.backend_interface.save_model(model_name)
                self.add_message(f"模型已保存: {saved_path}")

    def _load_model(self):
        """加载模型"""
        if self.backend_interface:
            filename = filedialog.askopenfilename(
                title="加载模型",
                filetypes=[("模型文件", "*.pth"), ("所有文件", "*.*")]
            )
            if filename:
                self.backend_interface.load_model(filename)
                self.add_message(f"模型已加载: {filename}")

    def _toggle_frozen(self):
        """切换模型冻结状态"""
        self.model_frozen = self.frozen_var.get()
        status = "冻结" if self.model_frozen else "解冻"
        self.add_message(f"模型已{status}")

    # =========================
    # 回放控制方法
    # =========================

    def _open_replay(self):
        """打开回放文件"""
        filename = filedialog.askopenfilename(
            title="打开回放文件",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        if filename:
            self._load_replay_file(filename)

    def _load_replay_file(self, filepath: str):
        """加载回放文件"""
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                replay_data = json.load(f)

            self.replay_frames = replay_data.get("frames", [])

            if not self.replay_frames:
                self.add_message("回放文件为空")
                return

            self.playback_mode = True
            self.is_playing = False
            self.replay_index = 0

            # 更新进度条
            self.replay_progress.config(to=len(self.replay_frames) - 1)
            self.replay_progress.set(0)

            # 显示第一帧
            self._show_replay_frame()

            self.add_message(f"已加载回放: {len(self.replay_frames)} 帧")

        except Exception as e:
            self.add_message(f"加载回放失败: {e}")

    def _play_replay(self):
        """播放回放"""
        if self.playback_mode and self.replay_frames:
            self.is_playing = True
            self.add_message("回放播放中")

    def _pause_replay(self):
        """暂停回放"""
        self.is_playing = False
        self.add_message("回放已暂停")

    def _stop_replay(self):
        """停止回放"""
        self.playback_mode = False
        self.is_playing = False
        self.replay_frames = []
        self.replay_index = 0
        self.add_message("回放模式已退出")

    def _on_replay_seek(self, value):
        """回放进度条拖动"""
        if self.playback_mode and self.replay_frames:
            self.replay_index = int(value)
            self._show_replay_frame()

    def _show_replay_frame(self):
        """显示当前回放帧"""
        if not self.playback_mode or not self.replay_frames:
            return

        if 0 <= self.replay_index < len(self.replay_frames):
            frame = self.replay_frames[self.replay_index]
            self.current_game_state = frame.get("game_state", {})
            self._render_game_state()

    def _update_replay(self):
        """更新回放播放"""
        if self.playback_mode and self.is_playing and self.replay_frames:
            if self.replay_index < len(self.replay_frames) - 1:
                self.replay_index += 1
                self.replay_progress.set(self.replay_index)
                self._show_replay_frame()
            else:
                self.is_playing = False
                self.add_message("回放播放完成")

    # =========================
    # 渲染和更新方法
    # =========================

    def _render_game_state(self):
        """渲染游戏状态到画布"""
        if not self.pygame_initialized or not self.renderer or not self.current_game_state:
            return

        try:
            # 渲染到pygame surface
            self.renderer.render(self.pygame_surface, self.current_game_state)

            # 转换为PIL图像然后转为PhotoImage
            pygame_image = pygame.image.tostring(self.pygame_surface, 'RGB')
            from PIL import Image, ImageTk

            pil_image = Image.frombytes('RGB', (self.game_width, self.game_height), pygame_image)
            photo = ImageTk.PhotoImage(pil_image)

            # 更新画布
            self.tk_canvas.delete("all")
            self.tk_canvas.create_image(self.game_width // 2, self.game_height // 2, image=photo)
            self.tk_canvas.image = photo  # 保持引用

        except Exception as e:
            print(f"渲染错误: {e}")
            # 显示错误信息
            self.tk_canvas.delete("all")
            self.tk_canvas.create_text(
                self.game_width // 2, self.game_height // 2,
                text=f"渲染错误: {str(e)[:50]}...",
                fill="red",
                font=("Arial", 12)
            )

    def _start_update_thread(self):
        """启动更新线程"""

        def update_loop():
            last_replay_time = time.time()

            while self.running and self.pygame_initialized:
                try:
                    # 更新游戏状态
                    if not self.playback_mode and self.backend_interface:
                        state = self.backend_interface.get_game_state()
                        if state:
                            self.current_game_state = state

                    # 更新统计信息
                    if self.backend_interface:
                        stats = self.backend_interface.get_training_stats()
                        if stats:
                            self.update_stats(stats)

                    # 更新回放
                    if self.is_playing and time.time() - last_replay_time > (0.1 / self.playback_speed):
                        self._update_replay()
                        last_replay_time = time.time()

                    # 渲染游戏画面
                    if self.current_game_state:
                        self.root.after_idle(self._render_game_state)

                    time.sleep(0.05)  # 20 FPS

                except Exception as e:
                    print(f"更新线程错误: {e}")
                    time.sleep(1.0)

        self.update_thread = threading.Thread(target=update_loop, daemon=True)
        self.update_thread.start()

    # =========================
    # 工具方法
    # =========================

    def add_message(self, message: str):
        """添加消息到日志"""
        timestamp = time.strftime("%H:%M:%S")
        formatted_msg = f"[{timestamp}] {message}"

        self.message_log.append(formatted_msg)
        if len(self.message_log) > self.max_messages:
            self.message_log.pop(0)

        if self.message_text:
            self.message_text.insert(tk.END, formatted_msg + "\n")
            self.message_text.see(tk.END)

        print(formatted_msg)

    def _clear_messages(self):
        """清除消息日志"""
        if self.message_text:
            self.message_text.delete(1.0, tk.END)
        self.message_log.clear()

    def update_stats(self, stats: Dict[str, Any]):
        """更新统计信息"""
        for key, value in stats.items():
            if key in self.stats_vars:
                try:
                    formatted_value = f"{value:.3f}" if isinstance(value, float) else str(value)
                    self.stats_vars[key].set(formatted_value)
                except:
                    pass

    def set_backend_interface(self, backend_interface):
        """设置后端接口"""
        self.backend_interface = backend_interface
        self.add_message("后端接口已连接")

    def bind_keyboard_events(self):
        """绑定键盘事件"""
        self.root.bind('<Key>', self._on_key_press)
        self.root.focus_set()

    def _on_key_press(self, event):
        """处理键盘事件"""
        if not self.backend_interface or self.ai_control or self.playback_mode:
            return

        action_map = {
            'Up': 3, 'Down': 1, 'Left': 2, 'Right': 0,
            'w': 3, 's': 1, 'a': 2, 'd': 0
        }

        if event.keysym in action_map:
            action = action_map[event.keysym]
            self.backend_interface.execute_action(action)
            action_names = ["右", "下", "左", "上"]
            self.add_message(f"执行动作: {action_names[action]}")
        elif event.keysym == 'r':
            self._reset_game()
        elif event.keysym == 'space':
            self._toggle_control()

    def run(self):
        """运行界面"""
        try:
            self.bind_keyboard_events()
            self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
            self.root.mainloop()
        except Exception as e:
            print(f"界面运行错误: {e}")

    def on_closing(self):
        """窗口关闭事件"""
        self.running = False
        if self.update_thread:
            self.update_thread.join(timeout=1)
        if pygame.get_init():
            pygame.quit()
        self.root.destroy()